#include <algorithm>
#include <kuai/ct.h>


namespace kuai {
	
	ConnectTable::ConnectTable(const Index v[])  {
		_nAtoms = v[0];
		_nBonds = v[1];
		_atoms = v+2;
		_bonds = _atoms + _nAtoms;
		_degree = _bonds + 2*_nBonds;
		_offset = _degree + _nAtoms;
		_adjs = _offset + _nAtoms;
	}
	
	ConnectTable::ConnectTable(Index nAtoms, const Index atoms[], Index nBonds, const Index bonds[], Index buffer[])  
	{
		assert (nAtoms > 0);
		assert (nBonds > 0);

		_nAtoms = buffer[0] = nAtoms;
		_nBonds = buffer[1] = nBonds;

		Index* p = buffer+2;
		std::copy(atoms, atoms+nAtoms, p);
		_atoms = p;

		p += nAtoms;
		std::copy(bonds, bonds+2*nBonds, p);
		_bonds = p;

		p += 2*nBonds;
		std::fill(p, p+nAtoms, 0);
		for (Index i = 0; i < nBonds*2; ) {
			Index a1 = bonds[i++];
			Index a2 = bonds[i++];
			assert (a1 < nAtoms);
			assert (a2 < nAtoms);
			p[a1] += 1;
			p[a2] += 1;
		}
		_degree = p;

		p += nAtoms;
		p[0] = 0;
		for (Index i = 0; i < nAtoms-1; ++i) {
			p[i+1] = p[i]+_degree[i];
		}
		_offset = p;

		p += nAtoms;
		std::fill(p, p+4*nBonds, INVALID_INDEX);
		for (Index i = 0; i < nBonds*2; ) {
			Index a1 = bonds[i++];
			Index a2 = bonds[i++];
			_setup(p, a1, a2, i);
			_setup(p, a2, a1, i);
		}
		_adjs = p;

		p += 4*nBonds;
		p[0] = INVALID_INDEX;
	}

	Index ConnectTable::count_size(Index nAtoms, Index nBonds) {
		return 3 + 3 * nAtoms + 6 * nBonds;
	}

	void ConnectTable::_setup(Index adjs[], Index atom1, Index atom2, Index bond) {
		Index* p;
		for (p=adjs+_offset[atom1]; *p == INVALID_INDEX; p += 2)
		{ }
		p[0] = atom2;
		p[1] = bond;
	}

	BigIndex count_masks(Index nAtoms) {
		BigIndex result = nAtoms;
		result = result * (result-1) / 2 / 8 + 1;
		return result;
	}

	void set_mask(const Index atoms[], BigIndex nList, Index gap, Byte masks[]) {
		static const Byte flags[] = {1, 2, 4, 8, 16, 32, 64, 128};
		for (BigIndex i = 0; i < nList; i += gap) {		
			BigIndex a1 = atoms[i];
			BigIndex a2 = atoms[i+1];
			if (a1 < a2) {
				std::swap(a1, a2);
			}
			BigIndex offset = a1 * (a1-1) / 2 + a2;
			BigIndex bit = offset % 8; offset /= 8;
			masks[offset] |= flags[bit];
		}
	}

	bool test_bit(const Byte masks[], Index i, Index j) {
		assert (i > j);
		static const Byte flags[] = {1, 2, 4, 8, 16, 32, 64, 128};
		BigIndex offset = i;
		offset = offset *(offset -1 ) / 2 + j;
		Index bit = offset % 8; offset /= 8;

		return (masks[offset] & flags[bit]) != 0;
	}

	BigIndex offset(Index i, Index j) {
		if (i < j) {
			std::swap(i, j);
		}
		BigIndex result = i;
		result = result * (result-1) / 2 + j;
		return result;
	}

}
